#include "esp_system.h"
#include "driver/uart.h"
#include "esp_log.h"
#include "string.h"

#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/task.h"

#include "RCS_uart.h"
#include "find_string.h"
#include "my_ringbuffer.h"

const uart_port_t uart_num = UART_NUM_1;
static QueueHandle_t uart_default_queue;
static const char *UART_TAG = "TX";
#define RX_PIN 7
#define TX_PIN 6
void uart_driver_init(void)
{
  uart_config_t uart_config = {
      .baud_rate = 115200,
      .data_bits = UART_DATA_8_BITS,
      .parity = UART_PARITY_DISABLE,
      .stop_bits = UART_STOP_BITS_1,
      .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
      .source_clk = UART_SCLK_DEFAULT,
  };
  // Configure UART parameters
  ESP_ERROR_CHECK(uart_param_config(uart_num, &uart_config));
  ESP_ERROR_CHECK(uart_set_pin(UART_NUM_1, TX_PIN, RX_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
  const int uart_buffer_size = (1024 * 2);

  // Install UART driver using an event queue here
  ESP_ERROR_CHECK(uart_driver_install(UART_NUM_1, uart_buffer_size,
                                      uart_buffer_size, 10, &uart_default_queue, 0));
}

esp_err_t uart_set_baud(uint32_t baudrate)
{
  return uart_set_baudrate(uart_num, baudrate);
}

void uart_send_data(char *package, uint16_t len)
{
  uart_write_bytes(uart_num, package, len);
  // ESP_LOGI(UART_TAG, "uart_write_bytes:%d", length);
  // esp_log_buffer_char(UART_TAG, package, len);
}

static uart_rx_fifo_t rx_fifo = {
    .fifo = {0},
    .get_index = 0,
    .put_index = 0,
};

static rx_pbuf_t *uart_pbuf_alloc(uint16_t buffered_size)
{
  bool ret = false;
  rx_pbuf_t *p = calloc(1, sizeof(rx_pbuf_t) * 1);
  if (p != NULL)
  {
    p->databuf = calloc(1, sizeof(uint8_t) * (buffered_size));
    if (p->databuf != NULL)
    {
      p->datasize = buffered_size;
      ret = true;
    }
    else
    {
      free(p);
    }
  }
  if (ret)
  {
    return p;
  }
  else
  {
    return NULL;
  }
}

void uart_pbuf_free(rx_pbuf_t *p)
{
  if (p != NULL)
  {
    if (p->databuf != NULL)
    {
      free(p->databuf);
      p->databuf = NULL;
    }
    else
    {
      ESP_LOGE(UART_TAG, "p->databuf == null");
    }
    free(p);
  }
  else
  {
    ESP_LOGE(UART_TAG, "p == null");
  }
  p = NULL;
}

static void put_data_in_fifo(char *pack, uint16_t buffered_size)
{
  uint8_t put_index_temp = rx_fifo.put_index;
  if (++put_index_temp >= RX_FIFO_DEPTH)
  {
    put_index_temp = 0;
  }
  if (put_index_temp == rx_fifo.get_index)
  {
    ESP_LOGE(UART_TAG, "rx fifo full");
    return;
  }
  rx_pbuf_t *p = uart_pbuf_alloc(buffered_size);
  if (p != NULL)
  {
    memcpy(p->databuf, pack, buffered_size);
  }
  rx_fifo.fifo[rx_fifo.put_index] = p;
  rx_fifo.put_index++;
  if (rx_fifo.put_index >= RX_FIFO_DEPTH)
  {
    rx_fifo.put_index = 0;
  }
  return;
}

static rx_pbuf_t *get_data_from_fifo(void)
{
  if (rx_fifo.get_index == rx_fifo.put_index)
  {
    ESP_LOGE(UART_TAG, "rx fifo empty");
    return NULL;
  }
  rx_pbuf_t *p = rx_fifo.fifo[rx_fifo.get_index];
  if (++(rx_fifo.get_index) >= RX_FIFO_DEPTH)
  {
    rx_fifo.get_index = 0;
  }
  return p;
}

#define UART_INFORM_Q_LENGTH RX_FIFO_DEPTH
#define UART_INFORM_Q_ITEM_SIZE 1 // 只发一个字节数据
QueueHandle_t uart_inform_queue;

enum
{
  CHECK_HEAD = 0,
  CHECK_END = 1,
};

#define REC_BUFFER_SIZE 2048
static char rec_buffer[REC_BUFFER_SIZE] = {0};

// 2023.4.21
static lwrb_t lwrb;

static const char *EVENT_TAG = "E";

static uint8_t need_check_flag = CHECK_HEAD;
static char package_head[3] = "\r\n";
static char package_end[3] = "\r\n";

void put_rec_buffer_to_fifo(char *newdata, int newlen, bool is_whole)
{
  int ret = 1;
  if (is_whole)
  {
    // 然后全部装进pbuf。
    // ESP_LOGE("fifo", "put %d bytes into fifo", newlen);
    // esp_log_buffer_char("fifo whole", rec_buffer, rec_buffer_free_head);
    put_data_in_fifo(newdata, newlen);
    xQueueSend(uart_inform_queue, &ret, portMAX_DELAY);
  }
  else
  {
    lwrb_write(&lwrb, newdata, newlen);
  }
}

static void uart_event_task(void *pvParameters)
{
  uart_event_t event;
  for (;;)
  {
    // Waiting for UART event.
    // ESP_LOGI(EVENT_TAG, "Waiting for UART event.");
    if (xQueueReceive(uart_default_queue, (void *)&event, portMAX_DELAY))
    {
      switch (event.type)
      {
      // Event of UART receving data
      /*We'd better handler data event fast, there would be much more data events than
      other types of events. If we take too much time on data event, the queue might
      be full.*/
      case UART_DATA:
      {
        char dummy[REC_BUFFER_SIZE] = {0};
        int len = uart_read_bytes(uart_num, dummy, event.size, portMAX_DELAY);
        // ESP_LOGI(EVENT_TAG, "uart_read_bytes: %d", len);
        lwrb_write(&lwrb, dummy, event.size);
        memset(dummy, 0, event.size);
        size_t left_len = lwrb_read(&lwrb, dummy, REC_BUFFER_SIZE);
        char *p = dummy;
        // ESP_LOGI(EVENT_TAG, "lwrb_read: %d", left_len);
        while (left_len > 0)
        {
          switch (need_check_flag)
          {
          case CHECK_HEAD:
          {
            int cur_package_head = sunday(p, package_head);
            // ESP_LOGE(EVENT_TAG, "head idx:%d", cur_package_head);
            if (cur_package_head >= 0)
            {
              // ESP_LOGE(EVENT_TAG, "has head, goto check end");
              // 更新未检测部分起始位置。
              int _len = cur_package_head + strlen(package_head);
              p += _len;
              left_len -= _len;

              need_check_flag = CHECK_END;
            }
            else
            {
              // no head.说明全是噪音。结束检测
              ESP_LOGE(EVENT_TAG, "all noise, finish analyse");
              esp_log_buffer_hex("noise", p, left_len);
              lwrb_write(&lwrb, p, left_len);
              left_len = 0;
            }
            break;
          }
          case CHECK_END:
          {
            int cur_package_end = sunday(p, package_end);
            // ESP_LOGE(EVENT_TAG, "end idx:%d", cur_package_end);
            if (cur_package_end >= 0)
            {
              // 有用部分的长度装入recbuffer。
              //  ESP_LOGE(EVENT_TAG, "has end, put into recbuffer and put into fifo");
              put_rec_buffer_to_fifo(p, cur_package_end, true);

              int useful_len = cur_package_end + strlen(package_end);
              // 更新p位置。
              p += useful_len;
              left_len -= useful_len;
              need_check_flag = CHECK_HEAD;
            }
            else
            {
              // 没有找到结束标志，将数据装入recbuffer留着下次用。
              //  ESP_LOGE(EVENT_TAG, "no end, put into recbuffer");
              put_rec_buffer_to_fifo(p, left_len, false);
              left_len = 0;
            }
            break;
          }
          default:
            break;
          }
        }
        break;
      }
      case UART_FIFO_OVF:
        ESP_LOGI("RX", "hw fifo overflow");
        // If fifo overflow happened, you should consider adding flow control for your application.
        // The ISR has already reset the rx FIFO,
        // As an example, we directly flush the rx buffer here in order to read more data.
        uart_flush_input(UART_NUM_1);
        xQueueReset(uart_default_queue);
        break;
      default:
        ESP_LOGI(UART_TAG, "error event type: %d", event.type);
        break;
      }
    }
  }
  vTaskDelete(NULL);
}

static uart_t uart;

static void uart_rx_thread(void *arg)
{
  BaseType_t xStatus;
  uint8_t ret;
  rx_pbuf_t *dummy_p;
  while (1)
  {
    xStatus = xQueueReceive(uart_inform_queue, &ret, portMAX_DELAY);
    dummy_p = get_data_from_fifo();
    if (dummy_p == NULL)
    {
      continue;
    }
    // ESP_LOGE("RX", "get %d bytes from fifo", dummy_p->datasize);
    esp_log_buffer_char("RX", dummy_p->databuf, dummy_p->datasize);
    if (uart.rx_cb)
    {
      uart.rx_cb(dummy_p);
    }
  }
}
#define UART_EVENT_THR_PRI 16
#define UART_RX_THR_PRI 14

void init_uart(uart_rx_cb_t uart_rx_cb)
{
  uart_driver_init();
  uart_inform_queue = xQueueCreate(UART_INFORM_Q_LENGTH, sizeof(uint8_t));
  uart.rx_cb = uart_rx_cb;
  lwrb_init(&lwrb, rec_buffer, REC_BUFFER_SIZE);
  xTaskCreate(uart_event_task, "uart_event_task", 1024 * 10, NULL, UART_EVENT_THR_PRI, NULL);
  xTaskCreate(uart_rx_thread, "uart_rx_thread", 1024 * 10, NULL, UART_RX_THR_PRI, NULL);
}